﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Microsoft.VisualStudio.Modeling;
using System.Windows.Data;
using CodeDomHelper.Types;
using System.ComponentModel;
using Microsoft.RestrictedUsage.CSharp.Semantics;
using devtm.Editor.TypeSelector;
using Mono.Cecil;
using CustomTypes.Mono.Extensions;
using devtm.AutoMapper.CustomCode.TemplateAdd;

namespace devtm.AutoMapper
{

    public static class MappingHelper
    {

        [CLSCompliant(false)]
        public class RankList : Dictionary<string, Rank>
        {

            public LinkPolicy Policy { get; set; }
            public TypeReference LeftElement { get; set; }
            public TypeReference RightElement { get; set; }
            public CustomTypeRepository Repository { get; set; }


            public Rank Current { get; set; }
        }

        [CLSCompliant(false)]
        public class Rank
        {
            public MemberReference Source { get; set; }
            public MemberReference Destination { get; set; }
            public ItemMap Map { get; set; }

            public bool SourceCreated { get; set; }

            public TypeReference TypeSource { get; set; }


            public TypeReference TypeDestination { get; set; }
        }



        internal static void GenerateMapping(MappingSource source, bool refresh, CustomTypeRepository repository)
        {

            LinkPolicy policy = LinkPolicy.Get() ?? new LinkPolicy();

            if (repository.ContainingProject != null)
            {

                TypeElement left = source.TypeElement;
                Mapper mapper = source.Mapper;
                TypeElement right = mapper.MappingDestination.TypeElement;

                using (Transaction t = source.Mapper.Store.TransactionManager.BeginTransaction("Update mapping"))
                {

                    RankList l = GetSource(mapper, left, repository, policy);
                    FindDestination(right, l);

                    foreach (Rank r in l.Values)
                    {

                        l.Current = r;

                        MemberReference prop1 = null;

                        #region Assign Source
                        
                        if (r.Source != null)
                        {

                            prop1 = r.Source;
                            r.Map.SourceName = prop1.Name;
                            r.TypeSource = prop1.GetTypeFrom(l.LeftElement);
                            SetInfoTypeSource(r.Map, r.TypeSource);

                            r.Map.SourceType = TypeDefinitionHelper.FindTypeName(prop1, l.LeftElement as TypeDefinition);

                            PropertyDefinition pr = prop1 as PropertyDefinition;
                            if (pr != null && !pr.CanRead)
                                r.Map.Enable = false;

                        }
                        else
                            r.Map.SourceName = string.Empty;

                        #endregion

                        if (r.SourceCreated) // uniquement les éléments pas encores dans le mapper
                        {

                            #region Assign Destination
                            
                            if (r.Destination != null)
                            {

                                prop1 = r.Destination;
                                r.Map.TargetName = prop1.Name;
                                r.TypeDestination = prop1.GetTypeFrom(l.RightElement);
                                SetInfoTypeTarget(r.Map, r.TypeDestination);

                                r.Map.TargetType = TypeDefinitionHelper.FindTypeName(prop1, l.RightElement as TypeDefinition);

                                PropertyDefinition pr = prop1 as PropertyDefinition;
                                if (pr != null & !pr.CanWrite)
                                    r.Map.Enable = false;
                            }
                            else
                                r.Map.TargetName = string.Empty;

                            #endregion

                            r.Map.NeedMap = ResolveNeedMap(r.TypeSource) | ResolveNeedMap(r.TypeDestination);

                            if (r.Map.Label.Trim() == "->")
                                r.Map.Label = "Oups";

                            if (r.Map.Enable)
                                r.Map.Enable = !(String.IsNullOrEmpty(r.Map.SourceName) || String.IsNullOrEmpty(r.Map.TargetName));

                            if (r.Map.Enable)
                                FindConverter(r.Map, r.SourceCreated, l);
                        }
                        else
                        {
                            if (!r.Map.Enable)
                                {

                                    r.Map.Enable = !(String.IsNullOrEmpty(r.Map.SourceName) || String.IsNullOrEmpty(r.Map.TargetName));
                                
                                    if (r.Map.Enable)
                                            FindConverter(r.Map, r.SourceCreated, l);

                                }
                            
                        }
                    }

                    if (!refresh)
                        FindNotUsedMembers(mapper, l);

                    t.Commit();

                }


            }

        }



        //public static bool CanRead(this MemberInfo property)
        //{

        //    if (property.MemberType == MemberTypes.Field || property.MemberType != MemberTypes.Method)
        //        return true;

        //    else if (property.MemberType != MemberTypes.Property)
        //        return false;

        //    return (property as PropertyInfo).CanRead;

        //}

        //public static bool CanWrite(this MemberInfo property)
        //{

        //    if (property.MemberType == MemberTypes.Field)
        //        return true;

        //    else if (property.MemberType != MemberTypes.Property)
        //        return false;

        //    return (property as PropertyInfo).CanWrite;

        //}


        //public static TypeReference GetTypeFrom(this IMemberDefinition member)
        //{

        //    MemberReference m = member as MemberReference;

        //    if (m == null)
        //        return null;

        //    switch (m.MemberType)
        //    {

        //        case MemberTypes.Property:
        //            PropertyDefinition prop = member as PropertyDefinition;
        //            return prop.PropertyType;

        //        case MemberTypes.Field:
        //            FieldInfo field = member as FieldDefinition;
        //            return field.FieldType;

        //        case MemberTypes.Method:
        //              MethodInfo method = member as MethodInfo;
        //              return method.ReturnType;

        //        case MemberTypes.NestedType:
        //        case MemberTypes.TypeInfo:
        //              return member as Type;

        //        case MemberTypes.Constructor:
        //        case MemberTypes.Custom:
        //        case MemberTypes.Event:
        //        case MemberTypes.All:
        //        default:
        //              return null;

        //    }

        //}


        public static void SetInfoTypeSource(ItemMap item, TypeReference type)
        {
            // prise en chage des tableaux
            TypeDefinition typeSource = type.Resolve();

            item.SourceIsAbstract = typeSource.IsAbstract;
            item.SourceIsAnsiClass = typeSource.IsAnsiClass;
            item.SourceIsArray = type.IsArray;// Ce n'est pas une erreur
            if (type.IsArray)
                item.SourceItemType = typeSource.AssemblyQualifiedName;
            item.SourceIsClass = typeSource.IsClass;
            item.SourceIsEnum = typeSource.IsEnum;
            item.SourceIsInterface = typeSource.IsInterface;
            item.SourceIsNested = typeSource.IsNested;
            item.SourceIsPublic = typeSource.IsPublic;
            item.SourceIsValueType = typeSource.IsValueType;
            item.SourceIsSealed = typeSource.IsSealed;
            item.SourceIsSerializable = typeSource.IsSerializable;
            item.SourceIsNullable = typeSource.AssemblyQualifiedName.StartsWith("System.Nullable`1[[");
            item.SourceIsEnumerable = typeSource.GetInterface("IEnumerable", true) != null;

            if (item.SourceIsEnumerable)
            {
                TypeReference typeEnumerable = FindItemFromIEnumerable(type);

                if (typeEnumerable != null)
                    item.SourceItemType = typeEnumerable.AssemblyQualifiedName;

                else
                    item.SourceItemType = string.Empty;

            }

        }

        public static void SetInfoTypeTarget(ItemMap item, TypeReference type)
        {

            TypeDefinition typeTarget = type.Resolve();

            item.TargetIsAbstract = typeTarget.IsAbstract;
            item.TargetIsAnsiClass = typeTarget.IsAnsiClass;
            item.TargetIsArray = type.IsArray;// Ce n'est pas une erreur
            if (type.IsArray)
                item.TargetItemType = typeTarget.AssemblyQualifiedName;
            item.TargetIsClass = typeTarget.IsClass;
            item.TargetIsEnum = typeTarget.IsEnum;
            item.TargetIsInterface = typeTarget.IsInterface;
            item.TargetIsNested = typeTarget.IsNested;
            item.TargetIsPublic = typeTarget.IsPublic;
            item.TargetIsValueType = typeTarget.IsValueType;
            item.TargetIsSealed = typeTarget.IsSealed;
            item.TargetIsSerializable = typeTarget.IsSerializable;
            item.TargetIsNullable = typeTarget.AssemblyQualifiedName.StartsWith("System.Nullable`1[[");
            item.TargetIsEnumerable = typeTarget.GetInterface("IEnumerable", true) != null;

            if (item.TargetIsEnumerable)
            {
                TypeReference typeEnumerable = FindItemFromIEnumerable(type);

                if (typeEnumerable != null)
                    item.TargetItemType = typeEnumerable.AssemblyQualifiedName;

                else
                    item.TargetItemType = string.Empty;

            }

        }

        public static TypeReference FindItemFromIEnumerable(TypeReference type)
        {

            List<TypeReference> types = new List<TypeReference>();

            TypeDefinition tt = type.Resolve();

            if (tt.IsInterface)
                types.Add(type);

            if (tt != null)
                types.AddRange(tt.Interfaces.ToList());

                var r = FindItemFromIEnumerable(type, types);

                if (r == null && tt.BaseType != null)
                    r = FindItemFromIEnumerable(tt.BaseType);

                return r;

        }

        public static TypeReference FindItemFromIEnumerable(TypeReference type, IEnumerable<TypeReference> types)
        {

            foreach (TypeReference t in types)
            {

                if (t.FullName.StartsWith("System.Collections.Generic.IEnumerable`1<"))
                {

                    GenericInstanceType t2 = t as GenericInstanceType;
                    TypeReference types2 = t2.GenericArguments.First();

                    if (types2.IsGenericParameter)
                        types2 = TypeDefinitionHelper.GetTypeFrom(t, type, types2 as GenericParameter);

                    return types2;
                }

                var ii = t.Resolve();

                if (ii != null)
                {

                    IEnumerable<TypeReference> types2 = ii.Interfaces;
                    if (types2 != null && types2.Count() > 2)
                    {
                        TypeReference i = FindItemFromIEnumerable(type, types2);
                        if (i != null)
                            return i;
                    }
                }
            }

            return null;

        }



        public static void FindConverter(ItemMap item, bool sourceCreated, RankList rankList)
        {

            if (item.NeedMap)
            {

                if (item.TargetIsEnumerable)
                {

                    // System.Collections.Generic.IList
                    var t1 = BuildTypeExpression.Analyse(item.TargetType);

                    if (item.TargetIsAbstract | item.TargetIsInterface)
                    {

                    }
                    else
                    {

                        switch (t1.Namespace + "." + t1.Name)
                        {

                            case "System.Collections.Generic.List`1":
                            case "System.Collections.Generic.IList`1":
                                item.TypeCast = CastType.List;
                                return;

                            case "System.Collections.ObjectModel.Collection`1":
                            case "System.Collections.Generic.Collection`1":
                            case "System.Collections.Generic.ICollection`1":
                                item.TypeCast = CastType.Collection;
                                return;

                            case "System.Collections.ObjectModel.ObservableCollection`1":
                                item.TypeCast = CastType.ObservableCollection;
                                return;

                            case "System.Collections.Generic.HashSet`1":
                                item.TypeCast = CastType.Hashset;
                                return;

                            default:
                                item.TypeCast = CastType.Custom;
                                item.Enable = false;
                                return;

                        }
                    }

                }
                else if (item.TargetIsArray)
                {
                    item.TypeCast = CastType.Array;
                }
                else if (item.SourceName == "this" || item.TargetName == "this")
                    item.TypeCast = CastType.Custom;
                else
                    item.TypeCast = CastType.Map;

                return;
            }
            else if (item.SourceType == item.TargetType)
            {

                foreach (var type in tbTypeValue)
                    if (item.SourceType == type.FullName)
                    {
                        item.TypeCast = CastType.None;
                        return;
                    }
               
            }

            if (!FindCast(item, rankList))
                item.TypeCast = CastType.Custom;
            
        }


 
        public static bool FindCast(ItemMap item, RankList rankList)
        {

            if (item.SourceIsEnum || item.TargetIsEnum)
                if (FindCastEnum(item))
                    return true;

            if (FindCastImplicit(item, rankList))
                return true;

            if (FindCastExplicit(item))
                return true;
            
            if (FindCastTypeConverter(item, rankList))
                return true;

            if (FindCastIValueConverter(item, rankList))
                return true;

            if (FindCastIConverter(item, rankList))
                return true;

            return false;

        }

        public static string[] GetCastExplicit(string source, string target)
        {

            var lst = GetExplicitConverters();

            string key = source + "!" + target;

            if (lst.ContainsKey(key))
                return lst[key];

            return null;

        }
        
       
        private static Dictionary<string, string[]> _ExplicitConverters = new Dictionary<string, string[]>();
        private static Dictionary<string, string[]> GetExplicitConverters()
        {

            if (_ExplicitConverters.Count == 0)
            {

                string[] methds = new string[] { "ToBoolean", "ToByte", "ToChar", "ToDateTime", "ToDecimal", "ToDouble", "ToInt16", "ToInt32", "ToInt64", "ToSByte", "ToSingle", "ToString", "ToUInt16", "ToUInt32", "ToUInt64" };
                var methodes = typeof(Convert).GetMethods().Where(c => methds.Contains(c.Name) & c.GetParameters().Length == 1).ToList(); ;

                foreach (var item in methodes)
                {
                    var tb = item.GetParameters();
                    if (tb.Length == 1)
                    {
                        var p = tb[0];
                        if (p.ParameterType != typeof(object))
                            if (p.ParameterType != item.ReturnType || !tbTypeValue.Contains(p.ParameterType))
                                _ExplicitConverters.Add(p.ParameterType.FullName + "!" + item.ReturnType.FullName, new string[] { "System.Convert", item.Name });
                    }
                }
            }

            return _ExplicitConverters;

        }


        public static Type[] tbTypeValue = new Type[] { typeof(System.Byte), typeof(System.Double), typeof(System.Int16), typeof(System.Int32), typeof(System.DateTime),
            typeof(System.Int64), typeof(System.SByte), typeof(System.Single), typeof(System.UInt16), typeof(System.UInt32), typeof(System.UInt64), typeof(System.DateTime?),
            typeof(System.String), typeof(System.Char), typeof(string), typeof(sbyte), typeof(byte), typeof(char), 
            typeof(short), typeof(ushort), typeof(int), typeof(uint), typeof(long), typeof(ulong), typeof(float), typeof(double), typeof(Int16), 
            typeof(Int32), typeof(Int64), typeof(UInt16), typeof(UInt32), typeof(UInt64), typeof(bool), typeof(decimal), typeof(sbyte?), 
            typeof(byte?), typeof(char?), typeof(short?), typeof(ushort?), typeof(int?), typeof(uint?), typeof(long?), typeof(ulong?), 
            typeof(float?), typeof(double?), typeof(Int16?), typeof(Int32?), typeof(Int64?), typeof(UInt16?), typeof(UInt32?), typeof(UInt64?), 
            typeof(bool?), typeof(decimal?) };


        public static String[] tbTypeToString = new string[] { typeof(Guid).FullName, typeof(Int16).FullName, typeof(UInt16).FullName, typeof(short).FullName, typeof(ushort).FullName, typeof(Int32).FullName, typeof(UInt32).FullName, typeof(int).FullName, typeof(uint).FullName, typeof(Int64).FullName, typeof(UInt64).FullName, typeof(long).FullName, typeof(ulong).FullName, typeof(decimal).FullName, typeof(float).FullName, typeof(double).FullName };

        private static bool FindCastExplicit(ItemMap item)
        {

            var lst = GetExplicitConverters();
            string key = item.SourceType + "!" + item.TargetType;
            if (lst.ContainsKey(key))
            {
                item.TypeCast = CastType.Explicit;
                return true;
            }

            if (typeof(string).FullName == item.TargetType)
            {
                if (tbTypeToString.Contains(item.SourceType) || item.SourceIsNullable)
                {
                    item.TypeCast = CastType.Explicit;
                    return true;
                }
            }
            else
            {
                if (item.SourceType == typeof(Guid).FullName && item.TargetType == typeof(byte[]).FullName)
                {
                    item.TypeCast = CastType.Explicit;
                    return true;
                }
            }
            return false;

        }

        private static bool FindCastEnum(ItemMap item)
        {

            Type t1 = item.Mapper.MappingModel.FindType(item.SourceType);
            Type t2 = item.Mapper.MappingModel.FindType(item.TargetType);

            Type t = t2 != null && t2.IsEnum ? t1 : t2;

            if (t != null && tbTypeValue.Contains(t))
            {
                item.TypeCast = CastType.Explicit;
                return true;
            }

            return false;
        }

        public static bool FindCastImplicit(ItemMap item, RankList rankList)
        {

            Type t1 = Type.GetType(item.SourceType);
            Type t2 = Type.GetType(item.TargetType);

            if (t1 != null && t2 != null
                && t1.IsPrimitive && t2.IsPrimitive
                && t1 != typeof(System.Boolean) && t2 != typeof(System.Boolean)
                && t1 != typeof(System.IntPtr) && t2 != typeof(System.IntPtr)
                && t1 != typeof(System.UIntPtr) && t2 != typeof(System.UIntPtr))
            {
                item.TypeCast = CastType.Explicit;
                return true;
            }

            return FindCastImplicitForClass(item, rankList);
        }

        private static bool FindCastImplicitForClass(ItemMap item, RankList rankList)
        {

            TypeDefinition s1 = rankList.Repository.GetType(item.SourceType);

            if (s1 != null)
            {
                // recupere les methodes speciales
                MethodDefinition[] ms = s1.Methods.Where(c => !c.HasThis & c.IsPublic & c.IsRuntimeSpecialName).ToArray();

                foreach (MethodDefinition m in ms)
                    if (m.Name == "op_Implicit")
                        if (TestMethod(m, item, CastType.Implicit, rankList))
                            return true;

                foreach (MethodDefinition m in ms)
                    if (m.Name == "op_Explicit")
                        if (TestMethod(m, item, CastType.Explicit, rankList))
                            return true;
            }

            return false;
        }

        private static bool TestMethod(MethodDefinition m, ItemMap item, CastType c, RankList rankList)
        {

            ParameterDefinition[] p = m.Parameters.ToArray();
            if (p.Length != 1)
                return false;

            if (TestType(p[0].ParameterType, m.ReturnType, item.SourceType, item.TargetType, rankList))
            {
                item.TypeCast = c;
                return true;
            }

            return false;

        }

        private static bool TestType(TypeReference type1, TypeReference type2, string source, string target, RankList rankList)
        {

            string s = TypeDefinitionHelper.FindTypeName(type1, rankList.LeftElement as TypeDefinition);
            if (s != source && s != target)
                return false;

            s = TypeDefinitionHelper.FindTypeName(type2, rankList.RightElement as TypeDefinition);
            if (s != source && s != target)
                return false;

            return true;
        }

        public static bool FindCastIConverter(ItemMap item, RankList rankList)
        {

            string filtre = typeof(Verbatim.CustomConverters.IConverter).FullName;

            #region Assignation par un attribut

            var def = rankList.Current.Destination as IMemberDefinition;

            if (def != null)
                foreach (var attribute in def.CustomAttributes)
                    if (attribute.AttributeType.FullName == typeof(Verbatim.CustomConverters. ConverterAttribute).FullName)
                    {


                        foreach (var arg in attribute.ConstructorArguments)
                        {
                            switch (arg.Type.FullName)
                            {
                                case "System.Type":
                                    item.Cast = (arg.Value as TypeReference).AssemblyQualifiedName;
                                    break;
                                case "System.String":
                                    item.Cast = (arg.Value as string);
                                    break;
                            }
                            break;
                        }

                        item.TypeCast = CastType.TypeConverter;
                        return true;
                    }

            #endregion


            #region Assignation implicit

            var repository = rankList.Repository;
            IEnumerable<TypeDefinition> reps = null;
            if (!repository.GetFilter(filtre, out reps))
            {
                Predicate<TypeDefinition> predicate = type => { return type.IsClass & !type.IsAbstract & type.GetInterface(filtre, false) != null; };
                reps = repository.GetTypes(predicate).ToList();
                repository.RegisterFilter(filtre, reps);
            }

            foreach (TypeDefinition t1 in reps)
            {


                GenericInstanceType t3 = t1.BaseType as GenericInstanceType;

                var source = t3.GenericArguments[0];
                var target = t3.GenericArguments[1];

                if (rankList.Current != null)
                {
                    var s = rankList.Current.Source.GetTypeFrom(t3);

                    if (rankList.Current.Destination != null)
                    {
                        var t = rankList.Current.Destination.GetTypeFrom(t3);

                        if (s.Equals(source) && t.Equals(target))
                        {
                            item.TypeCast = CastType.IConverter;
                            item.Cast = t3.AssemblyQualifiedName;
                        }
                    }
                }

            }

            #endregion

            return false;
        }

        public static bool FindCastIValueConverter(ItemMap item, RankList rankList)
        {

            var def = rankList.Current.Destination as IMemberDefinition;

            if (def != null)
                foreach (var attribute in def.CustomAttributes)
                    if (attribute.AttributeType.FullName == typeof(ValueConversionAttribute).FullName)
                    {


                        foreach (var arg in attribute.ConstructorArguments)
                        {
                            switch (arg.Type.FullName)
                            {
                                case "System.Type":
                                    item.Cast = (arg.Value as TypeReference).AssemblyQualifiedName;
                                    break;
                                case "System.String":
                                    item.Cast = (arg.Value as string);
                                    break;
                            }
                            break;
                        }

                        item.TypeCast = CastType.TypeConverter;
                        return true;
                    }

            return false;

        }

        public static bool FindCastTypeConverter(ItemMap item, RankList rankList)
        {

            var def = rankList.Current.Destination as IMemberDefinition;

            if (def != null)
                foreach (var attribute in def.CustomAttributes)
                    if (attribute.AttributeType.FullName == typeof(TypeConverterAttribute).FullName)
                    {


                        foreach (var arg in attribute.ConstructorArguments)
                        {
                            switch(arg.Type.FullName)
                            {
                                case "System.Type":
                                    item.Cast = (arg.Value as TypeReference).AssemblyQualifiedName;
                                    break;
                                case "System.String":
                                    item.Cast = (arg.Value as string);
                                    break;
                            }
                            break;
                        }
 
                        item.TypeCast = CastType.TypeConverter;
                        return true;
                    }

            return false;
        }

        public static bool ResolveNeedMap(TypeReference type)
        {
            if (type == null)
                return false;

            var t = type.Resolve();

            if (t == null)
                return false;

            if (t.IsPrimitive || t.IsValueType || t.IsEnum || NewMapperHelper.types.Contains(type.FullName))
                return false;

            return true;

        }






        private static void FindNotUsedMembers(Mapper mapper, Dictionary<string, Rank> l)
        {
            List<ItemMap> l2 = new List<ItemMap>();
            foreach (ItemMap item in mapper.ItemMaps)
                if (!string.IsNullOrEmpty(item.SourceName))
                    if (!l.ContainsKey(item.SourceName))
                        l2.Add(item);

            foreach (ItemMap item in l2)
                item.Delete();
        }


        private static void FindDestination(TypeElement right, RankList l)
        {

            l.RightElement = l.Repository.GetType(right.AssemblyQualifiedName);

            if (l.RightElement == null || l.RightElement
                .Namespace == "System.Collections.Generic" || l.RightElement.Namespace == "System.Collections")
                return;

            foreach (var item in l)
            {
                string n = item.Key;

                foreach (PropertyDefinition prop in GetRecursiveProperties(l.RightElement))
                    if (prop.CanWrite && l.Policy.Match(prop.Name, n, MemberTypeEnum.Property))
                    {
                        item.Value.Destination = prop;
                        break;
                    }

                foreach (FieldDefinition prop in GetRecursiveFields(l.RightElement))
                    if (l.Policy.Match(prop.Name, n, MemberTypeEnum.Field))
                    {
                        item.Value.Destination = prop;
                        break;
                    }

            }
        }


        private static RankList GetSource(Mapper mapper, TypeElement left, CustomTypeRepository repository, LinkPolicy policy)
        {

            RankList l = new RankList() { Policy = policy };

            l.Repository = repository;
            l.LeftElement = repository.GetType(left.AssemblyQualifiedName);

            if (l.LeftElement == null || l.LeftElement.Namespace == "System.Collections.Generic" || l.LeftElement.Namespace == "System.Collections")
                return l;

            foreach (PropertyDefinition item in GetRecursiveProperties(l.LeftElement))
            {

                if (!string.IsNullOrEmpty(item.Name))
                {

                    //  comparer la liste des membres du declaring type
                    MemberReference[] m1 = item.DeclaringType.GetMembers(item.Name).ToArray();
                    if (m1 == null || m1.Length == 0)
                        continue;

                    if (!item.CanRead) // || item.GetIndexParameters().Length != 0
                        continue;

                    if (!l.ContainsKey(item.Name))
                    {

                        ItemMap m = FindSourceByName(mapper, item.Name);

                        bool Created = false;
                        if (m == null)
                        {
                            mapper.ItemMaps.Add((m = new ItemMap(mapper.Partition) { SourceTypeMember = MemberTypeEnum.Property }));
                            Created = true;
                        }

                        l.Add(item.Name, new Rank() { Source = item, Destination = null, Map = m, SourceCreated = Created });

                    }

                }
            }

            foreach (FieldDefinition item in GetRecursiveFields(l.LeftElement))
            {
                if (!string.IsNullOrEmpty(item.Name))
                {
                    ItemMap m = FindSourceByName(mapper, item.Name);

                    if (m == null)
                        mapper.ItemMaps.Add((m = new ItemMap(mapper.Partition) { SourceTypeMember = MemberTypeEnum.Field }));

                    l.Add(item.Name, new Rank() { Source = item, Destination = null, Map = m });
                }
            }

            return l;
        }


        private static IEnumerable<PropertyDefinition> GetRecursiveProperties(TypeReference type)
        {

            TypeDefinition t = type.Resolve();

            foreach (PropertyDefinition p in t.GetMembers(MemberTypes.Property))
                yield return p;

            //if (t.BaseType != null && t.BaseType.FullName != "System.Object")
            //    foreach (PropertyDefinition p1 in GetRecursiveProperties(t.BaseType.Resolve()))
            //        yield return p1;

        }

        private static IEnumerable<FieldDefinition> GetRecursiveFields(TypeReference type)
        {

            TypeDefinition t = type.Resolve();

            foreach (FieldDefinition p in t.GetMembers(MemberTypes.Field))
                if (!p.IsStatic & p.IsPublic)
                    yield return p;

            //if (t.BaseType != null && t.BaseType.FullName != "System.Object")
            //    foreach (FieldDefinition p in GetRecursiveFields(t.BaseType.Resolve()))
            //        yield return p;

        }

        /// <summary>
        /// Renoie un map par son nom si deja créé
        /// </summary>
        /// <param name="mapper"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        private static ItemMap FindSourceByName(Mapper mapper, string name)
        {
            foreach (ItemMap ItemMap in mapper.ItemMaps)
                if (ItemMap.SourceName == name)
                    return ItemMap;

            return null;

        }

    }
}
